/*
 * By BEST PLM Team.
 *
 * Author: quhy1
 * Date: 2014-11-17
 */
package com.webridge.plm.util;

import java.text.MessageFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.ptc.core.foundation.type.server.impl.TypeHelper;
import com.ptc.core.meta.classification.command.model.common.GetClassificationAssociationCommand;
import com.ptc.core.meta.common.AssociationIdentifier;
import com.ptc.core.meta.common.AssociationTypeIdentifier;
import com.ptc.core.meta.common.TypeIdentifier;
import com.ptc.core.meta.common.TypeInstanceIdentifier;
import com.ptc.core.meta.server.TypeIdentifierUtility;
import com.ptc.core.meta.type.common.TypeInstance;
import com.ptc.core.meta.type.common.TypeInstanceHelper;

import wt.access.AccessControlHelper;
import wt.access.AccessPermission;
import wt.doc.WTDocument;
import wt.doc.WTDocumentMaster;
import wt.enterprise.RevisionControlled;
import wt.fc.Identified;
import wt.fc.IdentityHelper;
import wt.fc.ObjectIdentifier;
import wt.fc.ObjectReference;
import wt.fc.Persistable;
import wt.fc.PersistenceHelper;
import wt.fc.PersistenceServerHelper;
import wt.fc.QueryResult;
import wt.fc.ReferenceFactory;
import wt.fc.WTObject;
import wt.fc.WTReference;
import wt.fc.collections.WTCollection;
import wt.fc.collections.WTHashSet;
import wt.fc.collections.WTSet;
import wt.fc.collections.WTValuedHashMap;
import wt.fc.collections.WTValuedMap;
import wt.folder.Folder;
import wt.folder.FolderHelper;
import wt.folder.Foldered;
import wt.inf.container.WTContainer;
import wt.inf.container.WTContainerRef;
import wt.log4j.LogR;
import wt.method.RemoteAccess;
import wt.method.RemoteMethodServer;
import wt.occurrence.OccurrenceHelper;
import wt.org.WTPrincipal;
import wt.part.PartType;
import wt.part.PartUsesOccurrence;
import wt.part.Quantity;
import wt.part.QuantityUnit;
import wt.part.Source;
import wt.part.WTPart;
import wt.part.WTPartDescribeLink;
import wt.part.WTPartHelper;
import wt.part.WTPartIDSeq;
import wt.part.WTPartMaster;
import wt.part.WTPartMasterIdentity;
import wt.part.WTPartReferenceLink;
import wt.part.WTPartSubstituteLink;
import wt.part.WTPartUsageLink;
import wt.pds.StatementSpec;
import wt.pom.PersistenceException;
import wt.query.ClassAttribute;
import wt.query.ConstantExpression;
import wt.query.QueryException;
import wt.query.QuerySpec;
import wt.query.SearchCondition;
import wt.query.TableColumn;
import wt.query.WhereExpression;
import wt.series.HarvardSeries;
import wt.series.MultilevelSeries;
import wt.session.SessionHelper;
import wt.session.SessionServerHelper;
import wt.team.TeamReference;
import wt.type.TypedUtility;
import wt.util.WTException;
import wt.util.WTPropertyVetoException;
import wt.vc.Iterated;
import wt.vc.VersionControlException;
import wt.vc.VersionControlHelper;
import wt.vc.VersionIdentifier;
import wt.vc.Versioned;
import wt.vc.config.ConfigHelper;
import wt.vc.config.LatestConfigSpec;
import wt.vc.struct.StructHelper;
import wt.vc.views.View;
import wt.vc.views.ViewHelper;
import wt.vc.views.ViewManageable;
import wt.vc.wip.CheckoutLink;
import wt.vc.wip.WorkInProgressHelper;

public class PartUtil implements RemoteAccess {

	private static final int[] DEFAULT_CONDITION_ARRAY = new int[] { 0 };

	private final static Logger LOGGER = LogR.getLogger(PartUtil.class
			.getName());

	/**
	 * constructor
	 */
	public PartUtil() {
	}

	/**
	 * process multi parts revise
	 * 
	 * @param mtmSets
	 * @param comment
	 * @return
	 * @throws WTException
	 */
	public static Map<String, WTPart> reviseWTParts(List<WTSet> mtmSets,
			String comment) throws WTException {
		Map<String, WTPart> result = new HashMap<String, WTPart>();
		try {
			if (mtmSets != null && !mtmSets.isEmpty()) {
				for (int i = 0, len = mtmSets.size(); i < len; i++) {
					WTSet mtms = mtmSets.get(i);
					if (mtms != null && !mtms.isEmpty()) {
						WTValuedMap map = VersionControlHelper.service
								.newVersions(mtms);
						if (map != null && !map.isEmpty()
								&& !map.wtValues().isEmpty()) {
							WTCollection set1 = new WTHashSet();
							Iterator itr1 = map.wtValues()
									.persistableIterator();
							while (itr1.hasNext()) {
								Object obj = itr1.next();
								WTPart mtm = null;
								if (obj instanceof WTReference) {
									mtm = (WTPart) ((WTReference) obj)
											.getObject();
								} else if (obj instanceof WTPart) {
									mtm = (WTPart) obj;
								}
								LOGGER.error("reviseWTParts===>obj from newVersions WTValuedMap: "
										+ obj);

								if (mtm != null) {
									VersionControlHelper.setNote(mtm, comment);
									set1.add(mtm);
								}
							}
							WTCollection set2 = PersistenceHelper.manager
									.save(set1);
							Iterator itr2 = set2.persistableIterator();
							while (itr2.hasNext()) {
								Object obj = itr2.next();
								WTPart mtm = null;
								if (obj instanceof WTReference) {
									mtm = (WTPart) ((WTReference) obj)
											.getObject();
								} else if (obj instanceof WTPart) {
									mtm = (WTPart) obj;
								}
								LOGGER.error("reviseWTParts===>obj from save WTCollection: "
										+ obj);

								if (mtm != null) {
									result.put(mtm.getNumber(), mtm);
								}
							}
						}
					}
				}
			}
		} catch (Exception e) {
			LOGGER.error(e.getLocalizedMessage(), e);
			throw new WTException(e, e.getLocalizedMessage());
		}
		return result;
	}

	/**
	 * get latest part with specified view and in specified several states
	 * 
	 * @param partNumber
	 * @param viewName
	 * @param states
	 * @return
	 * @throws WTException
	 */
	public static WTPart getPartByStateAndView(String partNumber,
			String viewName, List<String> states) throws WTException {
		View view = null;
		if (viewName != null && !"".equalsIgnoreCase(viewName)) {
			view = ViewHelper.service.getView(viewName);
		}
		WTPart part = null;
		QuerySpec qs = new QuerySpec(WTPart.class);
		SearchCondition sc = new SearchCondition(WTPart.class, WTPart.NUMBER,
				SearchCondition.EQUAL, partNumber);
		qs.appendWhere(sc, new int[] { 0 });
		if (view != null) {
			qs.appendAnd();
			sc = new SearchCondition(WTPart.class, "view.key.id",
					SearchCondition.EQUAL, view.getPersistInfo()
							.getObjectIdentifier().getId());
			qs.appendWhere(sc, new int[] { 0 });
		}
		if (states != null && !states.isEmpty()) {
			qs.appendAnd();
			qs.appendOpenParen();
			for (int i = 0; i < states.size(); i++) {
				String state = states.get(i);
				if (i > 0) {
					qs.appendOr();
				}
				sc = new SearchCondition(WTPart.class, WTPart.LIFE_CYCLE_STATE,
						SearchCondition.EQUAL, state);
				qs.appendWhere(sc, new int[] { 0 });
			}
			qs.appendCloseParen();
		}
		QueryResult qr = PersistenceHelper.manager.find((StatementSpec) qs);
		qr = new LatestConfigSpec().process(qr);
		if (qr.hasMoreElements()) {
			part = (WTPart) qr.nextElement();
		}
		return part;
	}

	/**
	 * 
	 * @param partName
	 * @param states
	 * @return
	 * @throws WTException
	 */
	public static List<WTPart> getPartByName(String partName, String state,
			String type) throws WTException {
		List<WTPart> partList = new ArrayList<WTPart>();
		View view = ViewHelper.service.getView(ViewConstants.DESIGN_VIEW);
		QuerySpec qs = new QuerySpec(WTPart.class);
		SearchCondition sc = new SearchCondition(WTPart.class, WTPart.NAME,
				SearchCondition.EQUAL, partName, false);
		qs.appendWhere(sc, new int[] { 0 });
		if (view != null) {
			qs.appendAnd();
			sc = new SearchCondition(WTPart.class, "view.key.id",
					SearchCondition.EQUAL, view.getPersistInfo()
							.getObjectIdentifier().getId());
			qs.appendWhere(sc, new int[] { 0 });
		}
		LOGGER.debug("StatementSpec=" + qs);
		QueryResult qr = PersistenceServerHelper.manager
				.query((StatementSpec) qs);
		qr = new LatestConfigSpec().process(qr);
		WTPart part = null;
		while (qr.hasMoreElements()) {
			part = (WTPart) qr.nextElement();
			String ibaType = TypedUtility.getTypeIdentifier(part).getTypename();
			String partState = part.getState().toString();
			LOGGER.debug("ibaType=" + ibaType + ",partState = " + partState);
			if (partState.indexOf(state) != -1 && ibaType.indexOf(type) != -1) {
				partList.add(part);
			}
		}
		return partList;
	}

	/**
	 * is Part Exist
	 * 
	 * @param strNumber
	 *            String
	 * @return boolean
	 * @throws WTException
	 */
	public static boolean isPartExist(String strNumber) throws WTException {
		WTPart wtpart = getPartByNumber(strNumber);
		if (wtpart == null) {
			return false;
		} else {
			return true;
		}
	}

	/**
	 * revise WTPart
	 * 
	 * @param wtpart
	 *            WTPart
	 * @return WTPart
	 * @throws WTException
	 */
	public static WTPart reviseWTPart(WTPart part, String comment)
			throws WTException {
		WTPart wtpart = part;
		try {
			if (wtpart == null) {
				return null;
			}
			WTContainer container = wtpart.getContainer();
			WTContainerRef containerRef = WTContainerRef
					.newWTContainerRef(container);
			TeamReference teamReference = wtpart.getTeamId();
			Folder oldFoler = FolderHelper.getFolder(wtpart);// wtpart.getFolderingInfo().getFolder();
			if (oldFoler == null) {
				String strLocation = wtpart.getLocation();
				oldFoler = FolderHelper.service.getFolder(strLocation,
						containerRef);
			}

			wtpart = (WTPart) wt.vc.VersionControlHelper.service
					.newVersion((wt.vc.Versioned) wtpart);
			if (wtpart != null) {
				wtpart.setTeamId(teamReference);
			}
			VersionControlHelper.setNote(wtpart, comment);
			wtpart.setContainer(container);

			FolderHelper.assignFolder((Foldered) wtpart, oldFoler);

			wtpart = (WTPart) PersistenceHelper.manager.save(wtpart);

			wtpart = (WTPart) PersistenceHelper.manager.refresh(wtpart);

		} catch (WTPropertyVetoException e) {
			LOGGER.error(e.getLocalizedMessage(), e);
			throw new WTException(e, e.getLocalizedMessage());
		}
		return wtpart;
	}

	/**
	 * Remove WTPartUsageLink between childPart and root
	 * 
	 * @param childPart
	 * @param root
	 * @throws WTException
	 */
	public static void removeUseLink(WTPart childPart, WTPart root)
			throws WTException {
		if (childPart == null || root == null) {
			return;
		}
		WTPartMaster partMaster = (WTPartMaster) childPart.getMaster();
		QuerySpec queryspec = new QuerySpec(WTPartUsageLink.class);
		queryspec.appendWhere(
				new SearchCondition(WTPartUsageLink.class,
						"roleAObjectRef.key", "=", PersistenceHelper
								.getObjectIdentifier(root)), new int[] { 0 });
		queryspec.appendAnd();
		queryspec.appendWhere(
				new SearchCondition(WTPartUsageLink.class,
						"roleBObjectRef.key", "=", PersistenceHelper
								.getObjectIdentifier(partMaster)),
				new int[] { 0 });
		QueryResult qr = PersistenceServerHelper.manager.query(queryspec);
		while (qr.hasMoreElements()) {
			WTPartUsageLink link = (WTPartUsageLink) qr.nextElement();
			PersistenceServerHelper.manager.remove(link);
		}
	}

	/**
	 * Get part object by number,default as design view.
	 * 
	 * @param partNo
	 *            part number
	 * @return part object
	 * @throws wt.util.WTException
	 *             Windchill exception
	 * @see wt.part.WTPart
	 */
	public static WTPart getPartByNumber(String partNo) throws WTException {
		WTPart part = null;
		if (!StringUtil.isEmpty(partNo)) {
			part = getPartByNoAndView(partNo, ViewConstants.DESIGN_VIEW);
		}
		LOGGER.debug("SQL part:" + part);
		return part;
	}

	/**
	 * Get part by number and view name
	 * 
	 * @param partNo
	 *            part number
	 * @param viewName
	 *            view name
	 * @return part
	 * @throws WTException
	 *             Windchill exception
	 */
	public static WTPart getPartByNoAndView(String partNo, String viewName)
			throws WTException {
		WTPart part = null;
		if (!StringUtil.isEmpty(partNo) && !StringUtil.isEmpty(viewName)) {
			if (RemoteMethodServer.ServerFlag) {
				StatementSpec stmtSpec = new QuerySpec(WTPart.class);
				WhereExpression where = new SearchCondition(WTPart.class,
						WTPart.NUMBER, SearchCondition.EQUAL,
						partNo.toUpperCase());
				QuerySpec querySpec = (QuerySpec) stmtSpec;
				querySpec.appendWhere(where, DEFAULT_CONDITION_ARRAY);
				querySpec.appendAnd();
				View view = ViewHelper.service.getView(viewName);
				ObjectIdentifier objId = PersistenceHelper
						.getObjectIdentifier(view);
				where = new SearchCondition(WTPart.class, ViewManageable.VIEW
						+ "." + ObjectReference.KEY, SearchCondition.EQUAL,
						objId);
				querySpec.appendWhere(where, DEFAULT_CONDITION_ARRAY);
				QueryResult qr = PersistenceServerHelper.manager
						.query(stmtSpec);
				if (qr.hasMoreElements()) {
					LatestConfigSpec configSpec = new LatestConfigSpec();
					qr = configSpec.process(qr);
					part = (WTPart) qr.nextElement();
				}
			} else {
				ReflectionUtil.invokeOnMethodServer("getPartByNoAndView",
						PartUtil.class, null, new Class[] { String.class,
								String.class },
						new Object[] { partNo, viewName });
			}
		}
		return part;
	}

	/**
	 * Get part by number and view name
	 * 
	 * @param partNo
	 *            part number
	 * @param viewName
	 *            view name
	 * @return part
	 * @throws WTException
	 *             Windchill exception
	 */
	public static WTPart getPartByNoViewState(String partNo, String viewName,
			String state) throws WTException {
		WTPart part = null;
		if (!StringUtil.isEmpty(partNo) && !StringUtil.isEmpty(viewName)) {
			if (RemoteMethodServer.ServerFlag) {
				StatementSpec stmtSpec = new QuerySpec(WTPart.class);
				WhereExpression where = new SearchCondition(WTPart.class,
						WTPart.NUMBER, SearchCondition.EQUAL,
						partNo.toUpperCase());
				QuerySpec querySpec = (QuerySpec) stmtSpec;
				querySpec.appendWhere(where, DEFAULT_CONDITION_ARRAY);
				querySpec.appendAnd();
				View view = ViewHelper.service.getView(viewName);
				ObjectIdentifier objId = PersistenceHelper
						.getObjectIdentifier(view);
				where = new SearchCondition(WTPart.class, ViewManageable.VIEW
						+ "." + ObjectReference.KEY, SearchCondition.EQUAL,
						objId);
				querySpec.appendWhere(where, DEFAULT_CONDITION_ARRAY);

				if (state != null && !"".equalsIgnoreCase(state)) {
					querySpec.appendAnd();
					querySpec.appendWhere(new SearchCondition(WTPart.class,
							WTPart.LIFE_CYCLE_STATE, SearchCondition.EQUAL,
							state), DEFAULT_CONDITION_ARRAY);
				}
				QueryResult qr = PersistenceServerHelper.manager
						.query(stmtSpec);
				if (qr.hasMoreElements()) {
					LatestConfigSpec configSpec = new LatestConfigSpec();
					qr = configSpec.process(qr);
					part = (WTPart) qr.nextElement();
				}
			} else {
				ReflectionUtil.invokeOnMethodServer("getPartByNoViewState",
						PartUtil.class, null, new Class[] { String.class,
								String.class, String.class }, new Object[] {
								partNo, viewName, state });
			}
		}
		return part;
	}

	/**
	 * Get part by name and view name
	 * 
	 * @param partName
	 *            part name
	 * @param viewName
	 *            view name
	 * @return part
	 * @throws WTException
	 *             Windchill exception
	 */
	public static WTPart getPartByNameAndView(String partName, String viewName1)
			throws WTException {
		WTPart part = null;
		String viewName = viewName1;
		if (!StringUtil.isEmpty(partName)) {
			if (RemoteMethodServer.ServerFlag) {
				StatementSpec stmtSpec = new QuerySpec(WTPart.class);
				WhereExpression where = new SearchCondition(WTPart.class,
						WTPart.NAME, SearchCondition.EQUAL, partName);
				QuerySpec querySpec = (QuerySpec) stmtSpec;
				querySpec.appendWhere(where, DEFAULT_CONDITION_ARRAY);
				querySpec.appendAnd();
				if (viewName == null || "".equals(viewName)) {
					viewName = ViewConstants.DESIGN_VIEW;
				}
				View view = ViewHelper.service.getView(viewName);
				ObjectIdentifier objId = PersistenceHelper
						.getObjectIdentifier(view);
				where = new SearchCondition(WTPart.class, ViewManageable.VIEW
						+ "." + ObjectReference.KEY, SearchCondition.EQUAL,
						objId);
				querySpec.appendWhere(where, DEFAULT_CONDITION_ARRAY);
				QueryResult qr = PersistenceServerHelper.manager
						.query(stmtSpec);
				if (qr.hasMoreElements()) {
					LatestConfigSpec configSpec = new LatestConfigSpec();
					qr = configSpec.process(qr);
					part = (WTPart) qr.nextElement();
				}
			} else {
				ReflectionUtil.invokeOnMethodServer("getPartByNameAndView",
						PartUtil.class, null, new Class[] { String.class,
								String.class }, new Object[] { partName,
								viewName });
			}
		}
		return part;
	}

	/**
	 * Get part master by part number
	 * 
	 * @param partNo
	 *            part number
	 * @return part master
	 * @throws WTException
	 *             Windchill exception
	 * @see wt.part.WTPartMaster
	 */
	public static WTPartMaster getPartMasterByNumber(String partNo)
			throws WTException {
		WTPartMaster partMaster = null;
		if (!StringUtil.isEmpty(partNo)) {
			if (RemoteMethodServer.ServerFlag) {
				StatementSpec stmtSpec = new QuerySpec(WTPartMaster.class);
				WhereExpression where = new SearchCondition(WTPartMaster.class,
						WTPartMaster.NUMBER, SearchCondition.EQUAL,
						partNo.toUpperCase());
				QuerySpec querySpec = (QuerySpec) stmtSpec;
				querySpec.appendWhere(where, DEFAULT_CONDITION_ARRAY);
				QueryResult qr = PersistenceServerHelper.manager
						.query(stmtSpec);
				if (qr.hasMoreElements()) {
					partMaster = (WTPartMaster) qr.nextElement();
				}
			} else {
				ReflectionUtil.invokeOnMethodServer("getPartMasterByNumber",
						PartUtil.class, null, new Class[] { String.class },
						new Object[] { partNo });
			}
		}
		return partMaster;
	}

	/**
	 * is Part Number Exist
	 * 
	 * @param strNumber
	 *            String
	 * @return boolean
	 * @throws WTException
	 */
	public static boolean isPartNumberExist(String partNo) throws WTException {
		boolean result = false;
		if (!StringUtil.isEmpty(partNo)) {
			if (RemoteMethodServer.ServerFlag) {
				StatementSpec stmtSpec = new QuerySpec(WTPartMaster.class);
				WhereExpression where = new SearchCondition(WTPartMaster.class,
						WTPartMaster.NUMBER, SearchCondition.EQUAL,
						partNo.toUpperCase());
				QuerySpec querySpec = (QuerySpec) stmtSpec;
				querySpec.appendWhere(where, DEFAULT_CONDITION_ARRAY);
				QueryResult qr = PersistenceServerHelper.manager
						.query(stmtSpec);
				if (qr.hasMoreElements()) {
					result = true;
				}
			} else {
				ReflectionUtil.invokeOnMethodServer("isPartNumberExist",
						PartUtil.class, null, new Class[] { String.class },
						new Object[] { partNo });
			}
		}
		return result;
	}

	/**
	 * Judge if the two given parts has usage link between them
	 * 
	 * @param parentPart
	 *            parent part
	 * @param childMaster
	 *            child part master
	 * @return has usage link or not
	 * @throws WTException
	 *             windchill exxception
	 * @see wt.part.WTPart
	 * @see wt.part.WTPartMaster
	 * @see wt.part.WTPartUsageLink
	 */
	public static boolean hasUsageLink(WTPart parentPart,
			WTPartMaster childMaster) throws WTException {
		boolean hasUsageLink = false;
		WTPartUsageLink usageLink = getPartUsageLink(parentPart, childMaster);
		if (usageLink != null) {
			hasUsageLink = true;
		}
		return hasUsageLink;
	}

	/**
	 * Create part usage link
	 * 
	 * @param parentPart
	 *            parent part
	 * @param childMaster
	 *            child part master
	 * @param quantity
	 *            quantity
	 * @param unit
	 *            unit
	 * @return usage link
	 * @throws WTException
	 *             Windchill exception
	 */
	public static WTPartUsageLink createUsageLink(WTPart parentPart,
			WTPartMaster childMaster, String quantity, String unit)
			throws WTException {
		WTPartUsageLink newLink = null;
		if (parentPart != null && childMaster != null) {
			newLink = WTPartUsageLink.newWTPartUsageLink(parentPart,
					childMaster);
			if (!StringUtil.isEmpty(quantity) && !StringUtil.isEmpty(unit)) {
				Quantity qt = new Quantity();
				Double quantityDouble;
				try {
					quantityDouble = Double.valueOf(quantity);
				} catch (NumberFormatException e) {
					LOGGER.error(e);
					throw new WTException(e, e.getLocalizedMessage());
				}
				qt.setAmount(quantityDouble);
				QuantityUnit qu = getQuantityUnit(unit);
				qt.setUnit(qu);
				newLink.setQuantity(qt);
			}
			PersistenceServerHelper.manager.insert(newLink);
		}
		return newLink;
	}

	/**
	 * Get quantity unit according to the given unit string
	 * 
	 * @param unit
	 *            unit
	 * @return quantity unit
	 */
	public static QuantityUnit getQuantityUnit(String unit) {
		QuantityUnit qu = null;
		if (!StringUtil.isEmpty(unit)) {
			QuantityUnit[] quantityUnitArray = QuantityUnit
					.getQuantityUnitSet();
			if (quantityUnitArray != null) {
				int size = quantityUnitArray.length;
				for (int i = 0; i < size; i++) {
					qu = quantityUnitArray[i];
					if (unit.equalsIgnoreCase(qu.toString())) {
						break;
					}
				}
			}
		}
		return qu;
	}

	/**
	 * Get part usage link
	 * 
	 * @param parentPart
	 *            parent part
	 * @param childMaster
	 *            child part master
	 * @return part usage link
	 * @throws WTException
	 *             Windchill exception
	 * @see wt.part.WTPart
	 * @see wt.part.WTPartMaster
	 * @see wt.part.WTPartUsageLink
	 */
	public static WTPartUsageLink getPartUsageLink(WTPart parentPart,
			WTPartMaster childMaster) throws WTException {
		WTPartUsageLink usageLink = null;
		if (parentPart != null && childMaster != null) {
			QueryResult queryresult = PersistenceHelper.manager.find(
					wt.part.WTPartUsageLink.class, parentPart,
					WTPartUsageLink.USED_BY_ROLE, childMaster);

			if (queryresult == null || queryresult.size() == 0) {
				usageLink = null;
			} else {
				usageLink = (WTPartUsageLink) queryresult.nextElement();
			}
		}
		return usageLink;
	}

	/**
	 * getLenovoIteratedUsageLink
	 * 
	 * @param WTPart
	 *            wtpart
	 * @param WTPartMaster
	 *            wtpartmaster
	 * @return LenovoIteratedUsageLink
	 * @throws WTException
	 */

	/**
	 * Get different view part
	 * 
	 * @param partNo
	 *            part number
	 * @param view
	 *            part view
	 * @return part
	 * @throws WTException
	 *             Windchill exception
	 */
	public static WTPart getDiffViewPart(String partNo, String view)
			throws WTException {
		WTPart diffPart = null;
		WTPart part = getPartByNoAndView(partNo, view);
		if (part == null) {
			diffPart = getPartByNumber(partNo);
		}
		return diffPart;
	}

	/**
	 * Add reference doc for a part
	 * 
	 * @param part
	 *            part instance
	 * @param doc
	 *            document instance
	 * @throws WTException
	 *             Windchill exception
	 */
	public static void addReferenceDoc(WTPart part, WTDocument doc)
			throws WTException {
		if (RemoteMethodServer.ServerFlag) {
			WTPartReferenceLink refLink = getPartReferenceLink(part, doc);
			if (refLink == null) {
				WTDocumentMaster docMaster = (WTDocumentMaster) doc.getMaster();
				refLink = WTPartReferenceLink.newWTPartReferenceLink(part,
						docMaster);
				PersistenceServerHelper.manager.insert(refLink);
			}
		} else {
			ReflectionUtil.invokeOnMethodServer("addReferenceDoc",
					PartUtil.class, null, new Class[] { WTPart.class,
							WTDocument.class }, new Object[] { part, doc });
		}
	}

	/**
	 * Get reference link from a part and a doc
	 * 
	 * @param part
	 *            part instance
	 * @param doc
	 *            doc instance
	 * @return reference link
	 * @throws WTException
	 *             Windchill exception
	 */
	public static WTPartReferenceLink getPartReferenceLink(WTPart part,
			WTDocument doc) throws WTException {
		WTPartReferenceLink refLink = null;
		if (part != null && doc != null) {
			WTDocumentMaster docMaster = (WTDocumentMaster) doc.getMaster();
			refLink = getPartReferenceLink(part, docMaster);
		}
		return refLink;
	}

	public static WTPartReferenceLink getPartReferenceLink(WTPart part,
			WTDocumentMaster docMaster) throws WTException {
		WTPartReferenceLink refLink = null;
		if (part != null && docMaster != null) {
			if (RemoteMethodServer.ServerFlag) {
				QueryResult queryresult = PersistenceServerHelper.manager
						.query(WTPartReferenceLink.class, part,
								WTPartReferenceLink.REFERENCED_BY_ROLE,
								docMaster);
				if (queryresult.hasMoreElements()) {
					refLink = (WTPartReferenceLink) queryresult.nextElement();
				}
			} else {
				ReflectionUtil.invokeOnMethodServer("getPartReferenceLink",
						PartUtil.class, null, new Class[] { WTPart.class,
								WTDocumentMaster.class }, new Object[] { part,
								docMaster });
			}
		}
		return refLink;
	}

	/**
	 * Get the Latest Part by PartMaster
	 * 
	 * @param partMaster
	 * @return
	 * @throws WTException
	 */
	public static WTPart getLatestPartByMaster(WTPartMaster partMaster)
			throws WTException {
		WTPart latestPart = null;
		QueryResult qr = VersionControlHelper.service
				.allIterationsOf(partMaster);
		if (qr.hasMoreElements()) {
			latestPart = (WTPart) qr.nextElement();
		}
		return latestPart;
	}

	/**
	 * rename the duplicate wtpart if the document name is like '00001234' ,
	 * change to 'DUP_00001234'
	 * 
	 * @param wtpart
	 * @param partName
	 * @throws WTException
	 */
	public static void renameLPartName(WTPart wtpart, String partName)
			throws WTException {
		WTPartMaster lPartMaster = (WTPartMaster) wtpart.getMaster();
		wt.org.WTPrincipal curprincipal = SessionHelper.manager.getPrincipal();
		boolean accessFlag = SessionServerHelper.manager
				.setAccessEnforced(false);
		WTPartMasterIdentity identity = WTPartMasterIdentity
				.newWTPartMasterIdentity(lPartMaster);
		try {
			identity.setName(partName);
			identity.setNumber(lPartMaster.getNumber());
		} catch (WTPropertyVetoException e) {
			LOGGER.error(e.getLocalizedMessage(), e);
			throw new WTException(e, e.getLocalizedMessage());
		}

		wt.session.SessionHelper.manager.setAdministrator();
		IdentityHelper.service.changeIdentity((Identified) lPartMaster,
				identity);
		wt.session.SessionHelper.manager.setPrincipal(curprincipal.getName());
		SessionServerHelper.manager.setAccessEnforced(accessFlag);

		wt.session.SessionHelper.manager.setPrincipal(curprincipal.getName());
		SessionServerHelper.manager.setAccessEnforced(accessFlag);

	}

	/**
	 * renNumber the duplicate wtpart
	 * 
	 * @param wtpart
	 * @param String
	 *            newNumber
	 * @throws WTException
	 */
	public static void reNumberWTPart(WTPart aPart, String newNumber)
			throws WTException, WTPropertyVetoException {
		Identified aIdentified = (Identified) aPart.getMaster();
		WTPartMasterIdentity aWTPartMasterIdentity;
		aWTPartMasterIdentity = (WTPartMasterIdentity) aIdentified
				.getIdentificationObject();
		aWTPartMasterIdentity.setNumber(newNumber);
		IdentityHelper.service.changeIdentity(aIdentified,
				aWTPartMasterIdentity);
	}

	/**
	 * get the usagelink of part
	 * 
	 * @param part
	 * @return
	 * @throws WTException
	 */
	public static QueryResult findUsageLink(WTPart part) throws WTException {
		QueryResult qr = new QueryResult();
		QuerySpec queryspec = new QuerySpec(WTPartUsageLink.class);
		queryspec.appendWhere(
				new SearchCondition(WTPartUsageLink.class,
						"roleAObjectRef.key", "=", PersistenceHelper
								.getObjectIdentifier(part)), new int[] {});
		qr = PersistenceServerHelper.manager.query(queryspec);

		return qr;
	}

	/**
	 * remove all useLink
	 * @deprecated
	 * @param root
	 * @throws WTException
	 */
	public static void removeAllUseLink(WTPart root) throws WTException {
		QueryResult qr = WTPartHelper.service.getUsesWTParts(root,
				WTPartHelper.service.findWTPartConfigSpec());
		while (qr.hasMoreElements()) {
			Object[] obj = (Object[]) qr.nextElement();
			WTPartUsageLink link = (WTPartUsageLink) obj[0];
			PersistenceServerHelper.manager.remove(link);
		}
	}
	
	/**
	 * remove all useLink, optimized for
	 * a. better performance
	 * b. PTC official support API
	 * c. output debug enabled
	 * 
	 * @param root
	 * @throws WTException
	 */
	public static void removeAllUseLink(WTPart root, boolean isSupportedAPI) throws WTException {
		if(isSupportedAPI){
			QueryResult qr = WTPartHelper.service.getUsesWTParts(root,
					WTPartHelper.service.findWTPartConfigSpec());
			WTHashSet linksSet =  new WTHashSet();
			while (qr.hasMoreElements()) {
				Object[] obj = (Object[]) qr.nextElement();
				WTPartUsageLink link = (WTPartUsageLink) obj[0];
				if(link!=null){
					linksSet.add(link);
				}
			}
			if(linksSet!=null && linksSet.size()>0){
				LOGGER.debug("removeAllUseLink linksSet size&content:"+linksSet.size()+linksSet);
				PersistenceHelper.manager.delete(linksSet);
			}
		}else{
			removeAllUseLink(root);
		}
	}


	
	/**
	 * remove all LenovoIteratedUsageLink,optimized for
	 * a. better performance
	 * b. PTC official support API
	 * c. output debug enabled
	 * 
	 * @param root
	 * @throws WTException
	 */
	
	
	/**
	 * get WTPartSubstituteLink
	 * 
	 * @author chenbo
	 * @date 2012-05-7
	 * @param usageLink
	 * @param substitutePartMaster
	 * @return
	 * @throws WTException
	 */
	public static WTPartSubstituteLink getPartSubstituteLink(
			WTPartUsageLink usageLink, WTPartMaster substitutePartMaster)
			throws WTException {
		if (usageLink == null || substitutePartMaster == null) {
			return null;
		}
		Long long1 = Long.valueOf(PersistenceHelper.getObjectIdentifier(
				usageLink).getId());
		Long long2 = Long.valueOf(PersistenceHelper.getObjectIdentifier(
				substitutePartMaster).getId());
		QuerySpec qs1 = new QuerySpec(WTPartSubstituteLink.class);
		qs1.appendWhere(
				new wt.query.SearchCondition(WTPartSubstituteLink.class,
						"roleAObjectRef.key.id", "=", long1), new int[] { 0 });
		qs1.appendAnd();
		qs1.appendWhere(
				new wt.query.SearchCondition(WTPartSubstituteLink.class,
						"roleBObjectRef.key.id", "=", long2), new int[] { 0 });
		QueryResult qr = PersistenceServerHelper.manager.query(qs1);
		if (qr.hasMoreElements()) {
			return (WTPartSubstituteLink) qr.nextElement();
		}
		return null;
	}

	/**
	 * check the part whether is substitute part.
	 * 
	 * @param substitutePartMaster
	 * @return true/false
	 * @throws WTException
	 */
	public static boolean isSubstitutePart(WTPartMaster substitutePartMaster)
			throws WTException {
		Long masterOid = Long.valueOf(PersistenceHelper.getObjectIdentifier(
				substitutePartMaster).getId());
		QuerySpec qs1 = new QuerySpec(WTPartSubstituteLink.class);
		qs1.appendWhere(new wt.query.SearchCondition(
				WTPartSubstituteLink.class, "roleBObjectRef.key.id", "=",
				masterOid), new int[] { 0 });
		QueryResult qr = PersistenceServerHelper.manager.query(qs1);
		if (qr.hasMoreElements()) {
			return true;
		}
		return false;
	}
			
	public static QueryResult getSubstituteLinks(WTPartMaster substitutePartMaster)
			throws WTException {
		Long masterOid = Long.valueOf(PersistenceHelper.getObjectIdentifier(
				substitutePartMaster).getId());
		QuerySpec qs1 = new QuerySpec(WTPartSubstituteLink.class);
		qs1.appendWhere(new wt.query.SearchCondition(
				WTPartSubstituteLink.class, "roleBObjectRef.key.id", "=",
				masterOid), new int[] { 0 });
		QueryResult qr = PersistenceServerHelper.manager.query(qs1);
		return qr;
	}
	
	/**
	 * create WTPartSubstituteLink
	 * 
	 * @param link
	 * @param master
	 * @return
	 * @throws WTException
	 */
	public static WTPartSubstituteLink createSubstitutelink(
			WTPartUsageLink link, WTPartMaster master) throws WTException {
		if (link == null || master == null) {
			return null;
		}
		WTPartSubstituteLink substLink = WTPartSubstituteLink
				.newWTPartSubstituteLink(link, master);
		PersistenceServerHelper.manager.insert(substLink);
		return (WTPartSubstituteLink) PersistenceHelper.manager
				.refresh(substLink);
	}

	/**
	 * get part's parents
	 * 
	 * @param child
	 * @return List<WTPart>
	 * @throws WTException
	 */
	public static List<WTPart> getParents(WTPart child) throws WTException {
		List<WTPart> result = new ArrayList<WTPart>();
		QuerySpec queryspec = new QuerySpec(WTPart.class, WTPartUsageLink.class);
		queryspec.appendWhere(
				VersionControlHelper.getSearchCondition(WTPart.class, true),
				new int[] { 0, 1 });
		QueryResult qr = PersistenceHelper.manager.navigate(child.getMaster(),
				"usedBy", queryspec, true);
		LatestConfigSpec lcs = new LatestConfigSpec();
		qr = lcs.process(qr);
		while (qr.hasMoreElements()) {
			WTPart parent = (WTPart) qr.nextElement();
			result.add(parent);
		}
		return result;
	}

	public static List<WTPart> getParentsByView(WTPart child, String viewName)
			throws WTException {
		List<WTPart> result = new ArrayList<WTPart>();
		QuerySpec queryspec = new QuerySpec(WTPart.class, WTPartUsageLink.class);
		queryspec.appendWhere(
				VersionControlHelper.getSearchCondition(WTPart.class, true),
				new int[] { 0, 1 });
		if (StringUtils.isNotEmpty(viewName)) {
			queryspec.appendAnd();
			View view = ViewHelper.service.getView(viewName);
			ObjectIdentifier objId = PersistenceHelper
					.getObjectIdentifier(view);
			SearchCondition viewCondition = new SearchCondition(WTPart.class,
					ViewManageable.VIEW + "." + ObjectReference.KEY,
					SearchCondition.EQUAL, objId);
			queryspec.appendWhere(viewCondition, DEFAULT_CONDITION_ARRAY);
		}
		QueryResult qr = PersistenceHelper.manager.navigate(child.getMaster(),
				"usedBy", queryspec, true);
		LatestConfigSpec lcs = new LatestConfigSpec();
		qr = lcs.process(qr);
		while (qr.hasMoreElements()) {
			WTPart parent = (WTPart) qr.nextElement();
			result.add(parent);
		}
		return result;
	}

	/**
	 * get part by number and lifecycle state
	 * 
	 * @param number
	 * @param state
	 * @return
	 * @throws WTException
	 */
	@SuppressWarnings("deprecation")
	public static WTDocument getLatestWTDocByNumberState(String number,
			String state) throws WTException {
		if (number == null || number.length() == 0) {
			return null;
		}
		WTDocument result = null;
		QueryResult qr = null;
		boolean access = SessionServerHelper.manager.setAccessEnforced(false);
		try {
			QuerySpec querySpec = new QuerySpec(WTDocument.class);
			querySpec.appendWhere(
					new SearchCondition(WTDocument.class, WTDocument.NUMBER,
							SearchCondition.EQUAL, number.toUpperCase()),
					new int[] { 0 });
			querySpec.appendAnd();
			querySpec.appendWhere(new SearchCondition(WTDocument.class,
					WTDocument.LATEST_ITERATION, SearchCondition.IS_TRUE),
					new int[] { 0 });
			if (state != null && !"".equals(state)) {
				querySpec.appendAnd();
				querySpec.appendWhere(new SearchCondition(WTDocument.class,
						WTDocument.LIFE_CYCLE_STATE, SearchCondition.EQUAL,
						state), new int[] { 0 });
			}
			qr = PersistenceHelper.manager.find(querySpec);
			LatestConfigSpec cfg = new LatestConfigSpec();
			QueryResult qr1 = cfg.process(qr);
			if (qr1 != null && qr1.hasMoreElements()) {
				result = (WTDocument) qr1.nextElement();
			}
			return result;
		} finally {
			SessionServerHelper.manager.setAccessEnforced(access);
		}
	}

	public static WTPart addChildrenToPart(WTPart father, WTPart child,
			double quan, boolean ignoreDuplicated) throws WTException {
		WTPartMaster childMaster = (WTPartMaster) child.getMaster();
		if (ignoreDuplicated || !hasUsageLink(father, childMaster)) {
			WTPartUsageLink link = createUsageLink(father, childMaster, ""
					+ quan, "ea");
			LOGGER.debug("create link : " + link);
		}
		return father;
	}

	public static WTPart addChildrenToPart(WTPart father, WTPart child,
			double quan) throws WTException {
		WTPartMaster childMaster = (WTPartMaster) child.getMaster();
		if (!hasUsageLink(father, childMaster)) {
			WTPartUsageLink link = createUsageLink(father, childMaster, ""
					+ quan, "ea");
			LOGGER.debug("create link : " + link);
		}
		return father;
	}

	/**
	 * update usagelink quantity
	 * 
	 * @param father
	 * @param child
	 * @param quan
	 * @return
	 * @throws WTException
	 */
	public static WTPart updatePartChildrenQuantity(WTPart father,
			WTPart child, double quan) throws WTException {
		WTPartMaster childMaster = (WTPartMaster) child.getMaster();
		WTPartUsageLink link = getPartUsageLink(father, childMaster);
		if (link != null) {
			Quantity qt = link.getQuantity();
			qt.setAmount(quan);
			link.setQuantity(qt);
			PersistenceServerHelper.manager.update(link);
		}
		return father;
	}

	/**
	 * update LenovoIteratedUsageLink quantity
	 * 
	 * @param father
	 * @param child
	 * @param quan
	 * @return
	 * @throws WTException
	 * @throws WTPropertyVetoException 
	 */

	/**
	 * Gets the latest part instance by state.
	 * 
	 * @param number
	 *            part number
	 * @param states
	 *            valid state list
	 * @return
	 * @throws WTException
	 */
	public static WTPart getLatestPartByNumberAndState(String number,
			String... states) throws WTException {
		// LOGGER.debug("-----------getLatestPartByNumberAndState-------------"+number);
		if (number == null || number.equalsIgnoreCase("")) {
			// LOGGER.error("number is null");
			return null;
		}
		if (states.length == 0) {
			// LOGGER.error("Param states is null");
			return null;
		}

//		WTPart part = null;
		QuerySpec qs = new QuerySpec(WTPart.class);
		try {
			qs.setDescendantQuery(false);
			qs.setAdvancedQueryEnabled(true);
			int[] index = { 0 };
			WhereExpression sc = new SearchCondition(WTPart.class, WTPart.NUMBER,
					SearchCondition.EQUAL, number);
			qs.appendWhere(sc, index);
			qs.appendAnd();
			qs.appendOpenParen();
	
			for (int i = 0; i < states.length; i++) {
				// LOGGER.debug("-----------getLatestPartByNumberAndState-------------"+states[i]);
				WhereExpression sc2 = new SearchCondition(WTPart.class,
						"state.state", SearchCondition.EQUAL, states[i]);
				qs.appendWhere(sc2, index);
				// last sentence can not append appendOR
				if (i != states.length - 1) {
					qs.appendOr();
				}
			}
	
			qs.appendCloseParen();
			QueryResult qr = PersistenceHelper.manager.find((StatementSpec) qs);
			if(qr != null){
				LOGGER.debug("getLatestReleasedPartByNumber() qs = " + qs.toString() + "; qr.size()" + qr.size());
				
				LatestConfigSpec lcs = new LatestConfigSpec();
				qr = lcs.process(qr);
				while (qr.hasMoreElements()) {
					WTPart part = (WTPart) qr.nextElement();
					LOGGER.debug("getLatestReleasedPartByNumber() qr.nextElement() Number =  " + part.getNumber() );
					LOGGER.debug("getLatestReleasedPartByNumber() qr.nextElement() =  " + part.getVersionDisplayIdentifier() + "." + part.getIterationDisplayIdentifier());
					return part;
				}
			}
			/*ObjectVector oVector = new ObjectVector();
			ObjectVector markObjectVector = new ObjectVector();
			while (qr.hasMoreElements()) {
				WTPart elem = (WTPart) qr.nextElement();
				String view = elem.getViewName();
				LOGGER.error("FRU where used.part: "+elem.getNumber()+"\t"+elem.getIterationDisplayIdentifier().toString()+"\t"+view);
				if ("Design".indexOf(view) != -1) {
					// LOGGER.debug("-----------getLatestPartByNumberAndState-------------1");
					oVector.addElement(elem);
				}
				if ("Marketing".indexOf(view) != -1) {
					// LOGGER.debug("-----------getLatestPartByNumberAndState-------------2");
					markObjectVector.addElement(elem);
				}
			}
			qr = new QueryResult(oVector);
	
			while (qr.hasMoreElements()) {
				part = (WTPart) qr.nextElement();
			}
			if (part == null) {
				qr = new QueryResult(markObjectVector);
				qr = lcs.process(qr);
				while (qr.hasMoreElements()) {
					part = (WTPart) qr.nextElement();
				}
			}
			// LOGGER.debug("part: "+part.getNumber()+"\t"+part.getIterationDisplayIdentifier().toString());
			return part;*/
			
		} catch (WTPropertyVetoException e) {
			LOGGER.error(e.getLocalizedMessage(), e);
			e.printStackTrace();
		}
		
		return null;
	}


	/**
	 * Gets the latest production released part.
	 * 
	 * @param number
	 *            part number
	 * @author wangym7
	 * @return
	 * @throws WTException
	 */
	public static WTPart getLatestPRPartByNumber(String number)
			throws WTException {
		if (number == null || number.equalsIgnoreCase("")) {
			return null;
		}
//		WTPart part = null;
		QuerySpec qs = new QuerySpec(WTPart.class);
		try {
			qs.setDescendantQuery(false);
			qs.setAdvancedQueryEnabled(true);
			int[] index = { 0 };
			WhereExpression sc = new SearchCondition(WTPart.class, WTPart.NUMBER,
					SearchCondition.EQUAL, number);
			qs.appendWhere(sc, index);
			qs.appendAnd();
			qs.appendOpenParen();
	
			WhereExpression sc2 = new SearchCondition(WTPart.class, "state.state",
					SearchCondition.EQUAL, StateConstants.PRODUCTION_RELEASED);
			qs.appendWhere(sc2, index);
			qs.appendCloseParen();
			QueryResult qr = PersistenceHelper.manager.find((StatementSpec) qs);
			LOGGER.debug("getLatestReleasedPartByNumber() qs = " + qs.toString() + "; qr=" + qr);
			LatestConfigSpec lcs = new LatestConfigSpec();
			qr = lcs.process(qr);
			while (qr.hasMoreElements()) {
				return (WTPart) qr.nextElement();
			}
			/*
			ObjectVector oVector = new ObjectVector();
			ObjectVector markObjectVector = new ObjectVector();
			while (qr.hasMoreElements()) {
				WTPart elem = (WTPart) qr.nextElement();
				String view = elem.getViewName();
				if ("Design".indexOf(view) != -1) {
					oVector.addElement(elem);
				}
				if ("Marketing".indexOf(view) != -1) {
					markObjectVector.addElement(elem);
				}
			}
			qr = new QueryResult(oVector);
	
			LatestConfigSpec lcs = new LatestConfigSpec();
			qr = lcs.process(qr);
			while (qr.hasMoreElements()) {
				part = (WTPart) qr.nextElement();
			}
			if (part == null) {
				qr = new QueryResult(markObjectVector);
				qr = lcs.process(qr);
				while (qr.hasMoreElements()) {
					part = (WTPart) qr.nextElement();
				}
			}
			return part;**/
		} catch (WTPropertyVetoException e) {
			LOGGER.error(e.getLocalizedMessage(), e);
			e.printStackTrace();
		} 
		return null;
	}
	
	/**
	 * Gets the latest production released part.
	 * 
	 * @param number
	 *            part number
	 * @author zhangxc5
	 * @return
	 * @throws WTException
	 */
	public static WTPart getLatestReleasedPartByNumber(String number)
			throws WTException {
		if (number == null || number.equalsIgnoreCase("")) {
			return null;
		}
//		WTPart part = null;
		QuerySpec qs = new QuerySpec(WTPart.class);
		try {
			qs.setDescendantQuery(false);
			qs.setAdvancedQueryEnabled(true);
			int[] index = { 0 };
			WhereExpression sc = new SearchCondition(WTPart.class, WTPart.NUMBER,
					SearchCondition.EQUAL, number);
			qs.appendWhere(sc, index);
			qs.appendAnd();
			qs.appendOpenParen();
	
			WhereExpression sc2 = new SearchCondition(WTPart.class, "state.state",
					SearchCondition.EQUAL, StateConstants.PRODUCTION_RELEASED);
			qs.appendWhere(sc2, index);
			qs.appendCloseParen();
			QueryResult qr = PersistenceHelper.manager.find((StatementSpec) qs);
			
			LOGGER.debug("getLatestReleasedPartByNumber() qs = " + qs.toString() + "; qr=" + qr);
			
			LatestConfigSpec lcs = new LatestConfigSpec();
			qr = lcs.process(qr);
			while (qr.hasMoreElements()) {
				return (WTPart) qr.nextElement();
			}
			/*
			ObjectVector oVector = new ObjectVector();
			ObjectVector manuObjectVector = new ObjectVector();
			while (qr.hasMoreElements()) {
				WTPart elem = (WTPart) qr.nextElement();
				String view = elem.getViewName();
				if ("Design".indexOf(view) != -1) {
					oVector.addElement(elem);
				}
				if ("Manufacturing".indexOf(view) != -1) {
					manuObjectVector.addElement(elem);
				}
			}
			qr = new QueryResult(oVector);
	
			LatestConfigSpec lcs = new LatestConfigSpec();
			qr = lcs.process(qr);
			while (qr.hasMoreElements()) {
				part = (WTPart) qr.nextElement();
			}
			if (part == null) {
				qr = new QueryResult(manuObjectVector);
				qr = lcs.process(qr);
				while (qr.hasMoreElements()) {
					part = (WTPart) qr.nextElement();
				}
			}
			return part;**/
		} catch (WTPropertyVetoException e) {
			LOGGER.error(e.getLocalizedMessage(), e);
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * get latest permission part by part number and view
	 * 
	 * @param partNo
	 * @param viewName
	 * @return WTPart
	 * @throws WTException
	 * @author DaFeng
	 */
	public static WTPart getLatestPermissionPart(String partNo, String viewName)
			throws WTException {
		WTPart part = null;
		if (!StringUtil.isEmpty(partNo) && !StringUtil.isEmpty(viewName)) {
			if (RemoteMethodServer.ServerFlag) {
				StatementSpec stmtSpec = new QuerySpec(WTPart.class);
				WhereExpression where = new SearchCondition(WTPart.class,
						WTPart.NUMBER, SearchCondition.EQUAL,
						partNo.toUpperCase());
				QuerySpec querySpec = (QuerySpec) stmtSpec;
				querySpec.appendWhere(where, DEFAULT_CONDITION_ARRAY);
				querySpec.appendAnd();
				View view = ViewHelper.service.getView(viewName);
				ObjectIdentifier objId = PersistenceHelper
						.getObjectIdentifier(view);
				where = new SearchCondition(WTPart.class, ViewManageable.VIEW
						+ "." + ObjectReference.KEY, SearchCondition.EQUAL,
						objId);
				querySpec.appendWhere(where, DEFAULT_CONDITION_ARRAY);
				QueryResult qr = PersistenceHelper.manager
						.find((StatementSpec) querySpec);
				if (qr.hasMoreElements()) {
					LatestConfigSpec configSpec = new LatestConfigSpec();
					qr = configSpec.process(qr);
					part = (WTPart) qr.nextElement();
				}
			} else {
				ReflectionUtil.invokeOnMethodServer("getLatestPermissionPart",
						PartUtil.class, null, new Class[] { String.class,
								String.class },
						new Object[] { partNo, viewName });
			}
		}
		return part;
	}

	/**
	 * get substitutelink
	 * 
	 * @param usageLink
	 * @return
	 * @throws WTException
	 */
	public static QueryResult getPartSubstituteLink(WTPartUsageLink usageLink)
			throws WTException {

		QueryResult qr = null;
		try {
			Long long1 = Long.valueOf(PersistenceHelper.getObjectIdentifier(
					usageLink).getId());
			QuerySpec qs1 = new QuerySpec(WTPartSubstituteLink.class);
			qs1.appendWhere(new wt.query.SearchCondition(
					WTPartSubstituteLink.class, "roleAObjectRef.key.id", "=",
					long1), new int[] { 0 });
			qr = PersistenceServerHelper.manager.query(qs1);
		} catch (wt.query.QueryException e) {
			LOGGER.error(e.getLocalizedMessage(), e);
			throw new WTException(e, e.getLocalizedMessage());
		}
		return qr;
	}

	public static WTPart createPartBeforeSave(String number, String name,
			WTContainerRef containerRef, String softTypeFullName,
			Source partSource, PartType assembly, View view, String folder,
			String classificationPath, Map<String, String> attributes,
			WTPrincipal creator) throws WTException, WTPropertyVetoException {
		WTPart part = null;

		if (containerRef == null) {
			return null;
		}

		if (name == null || name.equalsIgnoreCase("")) {
			return null;
		}

		boolean flag = SessionServerHelper.manager.setAccessEnforced(false);
		WTPrincipal currentUser = SessionHelper.getPrincipal();
		try {
			if (creator != null) {
				SessionHelper.manager.setPrincipal(creator.getName());
			}

			if (number == null || number.equals("")) {
				number = PersistenceHelper.manager
						.getNextSequence(WTPartIDSeq.class);
				NumberFormat nf = NumberFormat.getInstance();
				nf.setMinimumIntegerDigits(16);
				nf.setMaximumIntegerDigits(16);
				nf.setGroupingUsed(false);
				number = nf.format(Integer.parseInt(number));
			}

			part = WTPart.newWTPart(number, name, QuantityUnit.EA);

			// set default type (Default : wt.part.WTPart)
			if (softTypeFullName == null || softTypeFullName.trim().equals("")) {
				softTypeFullName = "wt.part.WTPart";
			} else if (softTypeFullName.startsWith("WCTYPE|")) {
				softTypeFullName.substring(softTypeFullName.indexOf("|") + 1);
			}
			// set part type begin
			if (softTypeFullName != null) {
				TypeIdentifier id = TypeHelper
						.getTypeIdentifier(softTypeFullName);
				part = (WTPart) TypeHelper.setType(part, id);
			}

			// set context begin
			part.setContainerReference(containerRef);

			Folder location = null;
			if (folder != null && !"".equals(folder)) {
				if (!folder.startsWith("/")) {
					folder = "/" + folder;
				}
				if (!folder.startsWith("/Default")) {
					folder = "/Default" + folder;
				}
				location = FolderHelper.service.getFolder(folder, containerRef);
			} else {
				location = FolderHelper.service.getFolder("/Default",
						containerRef);
			}
			// set object to folder
			// if (location != null) {
			WTValuedHashMap map = new WTValuedHashMap();
			map.put(part, location);
			FolderHelper.assignLocations(map);
			// }

			// set default assembly attribute (Default : PartType.SEPARABLE)
			if (assembly == null) {
				assembly = PartType.SEPARABLE;
			}
			// set assembly begin
			part.setPartType(assembly);

			// set default source attribute (Default : Source.MAKE)
			if (partSource == null) {
				partSource = Source.MAKE;
			}
			// set source begin
			part.setSource(partSource);

			// set default view attribute (Default : Design)
			if (view == null) {
				view = ViewHelper.service.getView("Design");
			}
			// set view
			ViewHelper.assignToView(part, view);

			IBAUtil iba = new IBAUtil(part);

			Map<String, String> ibaMap = new HashMap<String, String>();

			// classification
			if (classificationPath != null && !"".equals(classificationPath)) {
				String clsPath = "wt.part.WTPart|" + classificationPath;
				ibaMap.put("Part", clsPath);
			}

			if (attributes != null && attributes.size() > 0) {
				ibaMap.putAll(attributes);
			}

			part = (WTPart) iba.updateIBAValue(part, ibaMap);
		} finally {
			SessionHelper.manager.setPrincipal(currentUser.getName());
			SessionServerHelper.manager.setAccessEnforced(flag);
		}

		return part;
	}

	public static void addClassificationTIIsToTI(TypeInstance ti,
			AssociationTypeIdentifier assoc, TypeInstanceIdentifier[] tiis,
			boolean isCreate) throws WTException {
		for (int i = 0; i < tiis.length; i++) {
			TypeInstanceIdentifier tii = tiis[i];
			// edit case : need to use acquireAssociationIdentifier instead of
			// acquireNewAssociationIdentifier
			AssociationIdentifier ai = null;
			if (isCreate) {
				ai = TypeInstanceHelper.acquireNewAssociationIdentifier(ti,
						assoc, tii);
			} else {
				ai = TypeInstanceHelper.acquireAssociationIdentifier(ti, assoc,
						tii);
			}
			ti.put(ai, tii);
		}
	}

	public static AssociationTypeIdentifier getClassificationAssociationTI(
			TypeInstance typeInstance) throws WTException {

		TypeIdentifier typeId = (TypeIdentifier) typeInstance.getIdentifier()
				.getDefinitionIdentifier();
		GetClassificationAssociationCommand cmd = new GetClassificationAssociationCommand();
		try {
			cmd.setClassifiedTypeIdentifier(typeId);
		} catch (WTPropertyVetoException wve) {
			throw new WTException(wve);
		}
		cmd = (GetClassificationAssociationCommand) cmd.execute();
		return cmd.getAssociation();
	}

	public static TypeInstanceIdentifier[] convertObjRefToTII(String[] formVal)
			throws WTException {
		if (formVal == null || (formVal != null && formVal.length < 1)) {
			return null;
		}

		TypeInstanceIdentifier[] tiiArray = new TypeInstanceIdentifier[formVal.length];
		for (int i = 0; i < formVal.length; i++) {
			String objRef = formVal[i];
			ReferenceFactory rf = new ReferenceFactory();
			WTReference ref = null;
			try {
				ref = rf.getReference(objRef);
			} catch (WTException e) {
				continue;
			}
			if (ref != null) {
				Persistable obj = ref.getObject();
				TypeInstanceIdentifier tiid = TypeIdentifierUtility
						.getTypeInstanceIdentifier(obj);
				tiiArray[i] = tiid;
			}
		}
		return tiiArray;
	}

	/**
	 * Get part by number, version, iteration, default view is design view
	 * 
	 * @author liuhaiquan
	 * @date 2014-3-24
	 * 
	 * @param number
	 *            version iteration
	 * @return part
	 * @throws WTException
	 */
	public static WTPart getPartByNumberVersionIteration(String number,
			String version, String iteration) throws WTException {
		QuerySpec query = new QuerySpec();
		query.setAdvancedQueryEnabled(true);
		int partTable = query.appendClassList(WTPart.class, true);
		int partMasterTable = query.appendClassList(WTPartMaster.class, false);
		String[] aliases = new String[2];
		aliases[0] = query.getFromClause().getAliasAt(partTable);
		aliases[1] = query.getFromClause().getAliasAt(partMasterTable);

		TableColumn sVersion = new TableColumn(aliases[0],
				"VERSIONIDA2VERSIONINFO");
		TableColumn sIter = new TableColumn(aliases[0],
				"ITERATIONIDA2ITERATIONINFO");
		TableColumn iDA3MASTERREFERENCE = new TableColumn(aliases[0],
				"IDA3MASTERREFERENCE");

		TableColumn numberCol = new TableColumn(aliases[1], "WTPARTNUMBER");
		TableColumn iDA2A2Col = new TableColumn(aliases[1], "IDA2A2");

		query.appendWhere(new SearchCondition(numberCol, SearchCondition.EQUAL,
				new ConstantExpression(number.toUpperCase())));
		query.appendAnd();

		query.appendWhere(new SearchCondition(iDA2A2Col, SearchCondition.EQUAL,
				iDA3MASTERREFERENCE));
		query.appendAnd();

		query.appendWhere(new SearchCondition(sVersion, SearchCondition.EQUAL,
				new ConstantExpression(version)));
		query.appendAnd();

		query.appendWhere(new SearchCondition(sIter, SearchCondition.EQUAL,
				new ConstantExpression(iteration)));

		query.appendAnd();
		View view = ViewHelper.service.getView(ViewConstants.DESIGN_VIEW);
		ObjectIdentifier objId = PersistenceHelper.getObjectIdentifier(view);
		SearchCondition where = new SearchCondition(WTPart.class,
				ViewManageable.VIEW + "." + ObjectReference.KEY,
				SearchCondition.EQUAL, objId);
		query.appendWhere(where, DEFAULT_CONDITION_ARRAY);

		QueryResult qr = PersistenceHelper.manager.find((StatementSpec) query);

		WTPart part = null;
		if (qr.hasMoreElements()) {
			Object obj[] = (Object[]) qr.nextElement();
			part = (WTPart) obj[0];
		}
		return part;
	}

	/**
	 * get part by number,version,iteration and viewName
	 * 
	 * @param number
	 * @param version
	 * @param iteration
	 * @param viewName
	 * @return
	 * @throws WTException
	 */
	public static WTPart getPartByNoVersionIterationView(String number,
			String version, String iteration, String viewName)
			throws WTException {
		QuerySpec query = new QuerySpec();
		query.setAdvancedQueryEnabled(true);
		int partTable = query.appendClassList(WTPart.class, true);
		int partMasterTable = query.appendClassList(WTPartMaster.class, false);
		String[] aliases = new String[2];
		aliases[0] = query.getFromClause().getAliasAt(partTable);
		aliases[1] = query.getFromClause().getAliasAt(partMasterTable);

		TableColumn sVersion = new TableColumn(aliases[0],
				"VERSIONIDA2VERSIONINFO");
		TableColumn sIter = new TableColumn(aliases[0],
				"ITERATIONIDA2ITERATIONINFO");
		TableColumn iDA3MASTERREFERENCE = new TableColumn(aliases[0],
				"IDA3MASTERREFERENCE");

		TableColumn numberCol = new TableColumn(aliases[1], "WTPARTNUMBER");
		TableColumn iDA2A2Col = new TableColumn(aliases[1], "IDA2A2");

		query.appendWhere(new SearchCondition(numberCol, SearchCondition.EQUAL,
				new ConstantExpression(number.toUpperCase())));
		query.appendAnd();

		query.appendWhere(new SearchCondition(iDA2A2Col, SearchCondition.EQUAL,
				iDA3MASTERREFERENCE));
		query.appendAnd();

		query.appendWhere(new SearchCondition(sVersion, SearchCondition.EQUAL,
				new ConstantExpression(version)));
		query.appendAnd();

		query.appendWhere(new SearchCondition(sIter, SearchCondition.EQUAL,
				new ConstantExpression(iteration)));

		query.appendAnd();
		View view = ViewHelper.service.getView(viewName);
		ObjectIdentifier objId = PersistenceHelper.getObjectIdentifier(view);
		SearchCondition where = new SearchCondition(WTPart.class,
				ViewManageable.VIEW + "." + ObjectReference.KEY,
				SearchCondition.EQUAL, objId);
		query.appendWhere(where, DEFAULT_CONDITION_ARRAY);

		QueryResult qr = PersistenceHelper.manager.find((StatementSpec) query);

		WTPart part = null;
		if (qr.hasMoreElements()) {
			Object obj[] = (Object[]) qr.nextElement();
			part = (WTPart) obj[0];
		}
		return part;
	}

	/**
	 * 
	 * this method is used to get working version of part.(check out part).
	 * 
	 * @param part
	 *            : WTPart need check out WTPart object
	 * @return WTPart : work copy of part
	 * @throws WTException
	 *             : exception handling
	 * @throws WTPropertyVetoException
	 *             : exception handling
	 * 
	 * 
	 */
	public static WTPart getWorkingCopyOfPart(WTPart part) throws WTException,
			WTPropertyVetoException {
		WTPart workingPart = null;
		if (!WorkInProgressHelper.isCheckedOut(part)) {
			Folder folder = WorkInProgressHelper.service.getCheckoutFolder();
			CheckoutLink checkoutlink = WorkInProgressHelper.service.checkout(
					part, folder, SessionHelper.manager.getPrincipal()
							.getName());
			workingPart = (WTPart) checkoutlink.getWorkingCopy();
		} else {
			if (!WorkInProgressHelper.isWorkingCopy(part)) {
				workingPart = (WTPart) WorkInProgressHelper.service
						.workingCopyOf(part);
			} else {
				workingPart = part;
			}
		}
		return workingPart;
	}

	/**
	 * Gets the reference designators on the usageLink.
	 * 
	 * @param usageLink
	 * @return
	 * @throws WTException
	 */
	public static String getReferenceDesignators(WTPartUsageLink usageLink)
			throws WTException {
		if (usageLink == null) {
			return null;
		}
		String returnStr = "";
		QueryResult queryresult = OccurrenceHelper.service
				.getUsesOccurrences(usageLink);
		for (int i = 0; i < queryresult.size(); i++) {
			PartUsesOccurrence partusesoccurrence = (PartUsesOccurrence) queryresult
					.nextElement();
			if (i == (queryresult.size() - 1)) {
				returnStr = returnStr + partusesoccurrence.getName();
			} else {
				returnStr = returnStr + partusesoccurrence.getName() + ",";
			}
		}
		return returnStr;
	}

	/**
	 * Find the children whose type is WTPart, and return the latest iteration.
	 * 
	 * @param parent
	 * @return
	 * @throws WTException
	 */
	public static List<WTPart> getChildren(WTPart parent) throws WTException {
		List<WTPart> children = new ArrayList<WTPart>();
		if (parent != null) {
			QueryResult qr = StructHelper.service.navigateUsesToIteration(
					parent, true, new LatestConfigSpec());
			while (qr.hasMoreElements()) {
				Iterated iterated = (Iterated) qr.nextElement();
				if (iterated instanceof WTPart) {
					children.add((WTPart) iterated);
				}
			}
		}
		return children;
	}
	
	//add by zhangxc5 to get latest production released child part and substitute part
		public static List<WTPart> getLatestReleasedChildrenAndSubstitue(WTPart parent) throws WTException {
			List<WTPart> children = new ArrayList<WTPart>();
			 LatestConfigSpec latestConfigSpec = new LatestConfigSpec();
			 if(parent!=null){
	            QueryResult usesWTParts = WTPartHelper.service.getUsesWTParts(parent, latestConfigSpec);
	         while (usesWTParts != null && usesWTParts.hasMoreElements()) {
	             Persistable persistable[] = (Persistable[]) usesWTParts.nextElement();
	             WTPartUsageLink usageLink = (WTPartUsageLink) persistable[0];
	             WTPart childPart = (WTPart) persistable[1];
	             if(!childPart.getLifeCycleState().toString().equals(StateConstants.PRODUCTION_RELEASED)){
	             	String partNo =childPart.getNumber();
	            	 	childPart=PartUtil.getLatestReleasedPartByNumber(partNo);
	              }
	             
	              children.add(childPart);

	             // Retrieves the substitutes related to the given WTPartUsageLink.
	             QueryResult substituteMasters = WTPartHelper.service.getSubstitutesWTPartMasters(usageLink);
	             while (substituteMasters != null && substituteMasters.hasMoreElements()) {
	                 WTPartMaster substituteMaster = (WTPartMaster) substituteMasters.nextElement();
	                 QueryResult qr = ConfigHelper.service.filteredIterationsOf(substituteMaster, latestConfigSpec);
	                 if (qr != null && qr.hasMoreElements()) {
	                     WTPart substitutePart = (WTPart) qr.nextElement();
	                     if(!substitutePart.getLifeCycleState().toString().equals(StateConstants.PRODUCTION_RELEASED)){
	                     	String subPartNo =substitutePart.getNumber();
	                     	substitutePart=PartUtil.getLatestReleasedPartByNumber(subPartNo);
	                      }
	                      
	                      children.add(substitutePart);
	                                           
	                     }
	                 }
	             }
	         }
			return children;
		}


	/**
	 * create LenovoIteratedUsageLink
	 * 
	 * @param wtpart
	 * @param wtpartby
	 * @param quantity
	 * @param unit
	 * @return
	 * @throws Exception
	 */
	
	
	 /**
     * checkOutPart
     * 
     * @param WTPart
     *            wtpart
     * @param String
     *            description
     * @return WTPart
     * @throws WTException
     */
    public static WTPart checkOutPart(WTPart wtpart1, String description)
            throws WTException {
        LOGGER.debug(">>>>>>>>>>>> LDocLoadsheetUtil.checkOutPart() begin----------------");
        WTPart wtpart = wtpart1;
        if (wtpart == null)
            return wtpart;
        wtpart = (WTPart) PersistenceHelper.manager.refresh(wtpart);
        if (!wtpart.isLatestIteration())
            wtpart = (WTPart) VersionControlHelper.service.getLatestIteration(
                    wtpart, false);
        wt.org.WTPrincipal wtprincipal = SessionHelper.manager.getPrincipal();
        // judge user modify access right.
        // AdminDomainRef adminDomainRef = wtpart.getDomainRef();
        if (SessionServerHelper.manager.isAccessEnforced()
                && !AccessControlHelper.manager.hasAccess(wtprincipal, wtpart,
                        AccessPermission.MODIFY)) {
            throw new WTException(wtprincipal.getName()
                    + " have no modify permission for part:"
                    + wtpart.getNumber() + " in "
                    + wtpart.getContainer().getName() + "/"
                    + wtpart.getFolderPath());
        }
        try {
            if (WorkInProgressHelper.isWorkingCopy(wtpart)) {
                LOGGER.debug(">>>>>>>>>>>> LDocLoadsheetUtil.checkOutPart() part is checkout copy"
                        + wtpart.getNumber());
                return wtpart;
            } else if (WorkInProgressHelper.isCheckedOut(wtpart)) {
                LOGGER.debug(">>>>>>>>>>>> LDocLoadsheetUtil.checkOutPart() part is checkout"
                        + wtpart.getNumber());

                return (WTPart) WorkInProgressHelper.service
                        .workingCopyOf(wtpart);

            } else {
                LOGGER.debug(">>>>>>>>>>>>  LDocLoadsheetUtil.checkOutPart() part is not check out,begin check out");
                LOGGER.debug("PersistenceHelper.isPersistent:"
                        + PersistenceHelper.isPersistent(wtpart));
                Folder folder = WorkInProgressHelper.service
                        .getCheckoutFolder();
                CheckoutLink checkoutLink = WorkInProgressHelper.service
                        .checkout(wtpart, folder, description);
                return (WTPart) checkoutLink.getWorkingCopy();
            }
        } catch (WTPropertyVetoException ex) {
            throw new WTException(ex);
        }
    }

    /**
     * checkInPart
     * 
     * @param WTPart
     *            wtpart
     * @param String
     *            description
     * @return WTPart
     * @throws WTException
     */
    public static WTPart checkInPart(WTPart wtpart1, String description)
            throws WTException {
        WTPart wtpart = wtpart1;
        if (wtpart != null) {
            LOGGER.debug("wtpart : " + wtpart.getNumber() + " - "
                    + wtpart.getName() + " version : "
                    + wtpart.getVersionDisplayIdentifier() + " . "
                    + wtpart.getIterationDisplayIdentifier() + " state : "
                    + wtpart.getState());
            wtpart = (WTPart) PersistenceHelper.manager.refresh(wtpart);
            if (!wtpart.isLatestIteration())
                wtpart = (WTPart) VersionControlHelper.service
                        .getLatestIteration(wtpart, false);
            LOGGER.debug("wtpart : " + wtpart.getNumber() + " - "
                    + wtpart.getName() + " version : "
                    + wtpart.getVersionDisplayIdentifier() + " . "
                    + wtpart.getIterationDisplayIdentifier() + " state : "
                    + wtpart.getState());
            LOGGER.debug("wtpart : " + wtpart.getNumber() + " - "
                    + wtpart.getName() + " workcopy : "
                    + WorkInProgressHelper.isWorkingCopy(wtpart)
                    + " checkout? " + WorkInProgressHelper.isCheckedOut(wtpart));
            try {
                if (WorkInProgressHelper.isWorkingCopy(wtpart)) {
                    wtpart = (WTPart) WorkInProgressHelper.service.checkin(
                            wtpart, description);
                } else if (WorkInProgressHelper.isCheckedOut(wtpart)) {
                    wtpart = (WTPart) WorkInProgressHelper.service
                            .workingCopyOf(wtpart);
                    wtpart = (WTPart) WorkInProgressHelper.service.checkin(
                            wtpart, description);
                }
            } catch (WTPropertyVetoException ex) {
                throw new WTException(ex);
            }
        }
        return wtpart;
    }
    
    
    /**
     * getWTPartReferenceLink
     * 
     * @param WTPart
     * @param WTDocument
     * @return WTPartReferenceLink
     */
    public static WTPartReferenceLink getWTPartReferenceLink(WTPart wtpart,
            WTDocument wtdoc) throws WTException {
        WTPartReferenceLink link = null;
        if (wtpart != null && wtdoc != null) {
            QuerySpec queryspec = new QuerySpec(WTPartReferenceLink.class);
            queryspec.appendWhere(new SearchCondition(
                    WTPartReferenceLink.class, "roleAObjectRef.key", "=",
                    PersistenceHelper.getObjectIdentifier(wtpart)),
                    new int[] { 0 });
            queryspec.appendAnd();
            queryspec.appendWhere(new SearchCondition(
                    WTPartReferenceLink.class, "roleBObjectRef.key", "=",
                    PersistenceHelper.getObjectIdentifier(wtdoc.getMaster())),
                    new int[] { 0 });
            QueryResult qr = PersistenceServerHelper.manager.query(queryspec);
            while (qr.hasMoreElements()) {
                link = (WTPartReferenceLink) qr.nextElement();
            }
        }
        return link;
    }
    
    
   
    /**
     * deleteWTPartDescribeLinks
     * 
     * @param WTPart
     * @param WTDocument
     * @return void
     */
    public static void deleteWTPartDescribeLinks(WTPart wtpart, WTDocument wtdoc)
            throws WTException {
        if (wtpart != null && wtdoc != null) {
            QueryResult qr = VersionControlHelper.service
                    .allIterationsFrom(wtdoc);
            while (qr.hasMoreElements()) {
                WTDocument doc = (WTDocument) qr.nextElement();
                WTPartDescribeLink link = getWTPartDescribeLink(wtpart, doc);
                if (link != null) {
                    PersistenceServerHelper.manager.remove(link);
                }
            }
        }
    }
    
    
    /**
     * getWTPartDescribeLink
     * 
     * @param WTPart
     * @param WTDocument
     * @return WTPartDescribeLink
     */
    public static WTPartDescribeLink getWTPartDescribeLink(WTPart wtpart,
            WTDocument wtdoc) throws WTException {
        if (wtpart == null || wtdoc == null)
            return null;
        QuerySpec queryspec = new QuerySpec(WTPartDescribeLink.class);
        queryspec.appendWhere(
                new SearchCondition(WTPartDescribeLink.class,
                        "roleAObjectRef.key", "=", PersistenceHelper
                                .getObjectIdentifier(wtpart)), new int[] { 0 });
        queryspec.appendAnd();
        queryspec.appendWhere(
                new SearchCondition(WTPartDescribeLink.class,
                        "roleBObjectRef.key", "=", PersistenceHelper
                                .getObjectIdentifier(wtdoc)), new int[] { 0 });
        QueryResult qr = PersistenceServerHelper.manager.query(queryspec);
        while (qr.hasMoreElements()) {
            WTPartDescribeLink link = (WTPartDescribeLink) qr.nextElement();
            return link;
        }
        return null;
    }
    /**
     * To determine whether the disabled parts
     * @param part
     * @return
     */
    public static boolean isDisbaled(WTPart part){
    	if(part==null){
    		return true;
    	}
    	WTContainer container=part.getContainer();
    	if(container==null){
    		return false;
    	}
    	String containerName=container.getName();
    	if(StringUtil.isEmpty(containerName)){
    		return false;
    	}
    	if(containerName.indexOf("Disabled_")>-1){
    		return true;
    	}
    	return false;
    }


  //added by fengst1 on 20161229 for SR
    /**
     * 
     * @param part
     * @param checkState
     * @return
     */
    public static WTPart getPreviousVersionPart(WTPart part, boolean checkState){
        WTPart rc = null;
         if (isFirstVersion(part)){
            return null;
         }else {
            QueryResult queryResult;
			try {
				queryResult = wt.vc.VersionControlHelper.service.allVersionsOf(part.getMaster());
	            WTPart tmp = null;
	            while (queryResult.hasMoreElements()) {
	                tmp = ((WTPart) queryResult.nextElement());
	                if (versionLessThan(tmp, part) && rc == null) {
	                    rc = tmp;
	                } else if (versionLessThan(tmp, part) && rc != null) {
	                    if (versionGreaterThan(tmp, rc)){
	                        rc = tmp;
	                    }
	                }
	            }
			} catch (PersistenceException e) {
				e.printStackTrace();
			} catch (WTException e) {
				e.printStackTrace();
			}
            if (rc != null) {
                if (checkState && !rc.getLifeCycleState().toString().equals("PRODUCTIONRELEASED")) {
                    rc = getPreviousVersionPart(rc, checkState);
                }
                return rc;
            } else{ 
                return rc;
            }
         }
     }
    
    /**
     * Determine if is the first version 
     * 
     * @param pbo
     * @return
     */
    public static boolean isFirstVersion(WTObject pbo) {
        if (pbo instanceof RevisionControlled) {
            Versioned versioned = (Versioned) pbo;
            String s = versioned.getVersionIdentifier().getValue();
            String s1 = "";
            try {
                wt.series.Series series = versioned.getVersionIdentifier().getSeries();
                if (series instanceof HarvardSeries) {
                    HarvardSeries harvardSeries = (HarvardSeries) series;
                    String seriesName = harvardSeries.getSeriesName();
                    HarvardSeries harvardSeries1 = new HarvardSeries(seriesName);
                    s1 = harvardSeries1.getValue();
                } else if (series instanceof MultilevelSeries) {
                    MultilevelSeries multilevelseries = MultilevelSeries.newMultilevelSeries("wt.vc.VersionIdentifier");
                    s1 = multilevelseries.getValue();
                } else {
                    s1 = VersionIdentifier.newVersionIdentifier().getValue();
                }
                return s.equals(s1);
            } catch (Exception ex) {
            	LOGGER.debug("Utility check isFirstVersion Failed");
            	LOGGER.error("", ex);
                return false;
            }
        } else {
        	LOGGER.debug("primaryBusinessObject is not a RevisionControlled Object");
            return false;
        }
    }
    
    public static boolean versionLessThan(Versioned a, Versioned b) throws VersionControlException {
        return (VersionControlHelper.getVersionIdentifier(a).getSeries().lessThan(VersionControlHelper
               .getVersionIdentifier(b).getSeries()));
    }
    
    public static boolean versionGreaterThan(Versioned a, Versioned b) throws VersionControlException {
        return (VersionControlHelper.getVersionIdentifier(a).getSeries().greaterThan(VersionControlHelper
               .getVersionIdentifier(b).getSeries()));
    }
  //end for SR

    /**
     * Filter invalid Part numbers which do not exist in system.
     * 
     * @param partNumsTobeFiltered
     *            the given collection of Part numbers to be filtered.
     * @param isMTNum
     *            if true, the MT number will be returned once any Part object starts with the MT number.
     * @return Returns valid Part numbers in the given collection, may be empty, will not be null.
     * @author Luo Jiao
     */
    public static Set<String> filterInvalidPartNums(Collection<String> partNumsTobeFiltered, boolean isMTNum) {
        LOGGER.debug("Enter => PartUtil.filterInvalidPartNums()");

        Set<String> validPartNums = new TreeSet<String>();
        StringBuffer sql = new StringBuffer();
        try {
            String selectField = "";
            if (isMTNum) {
                selectField = "substr(partMaster.WTPartNumber, 0, 4)";
            } else {
                selectField = "partMaster.WTPartNumber";
            }

            sql.append("select distinct ").append(selectField);
            sql.append("  from WTPartMaster partMaster");
            sql.append(" where ").append(selectField).append(" in (");
            int i = 0;
            for (String partNum : partNumsTobeFiltered) {
                sql.append("'").append(partNum).append("'");
                if (i < partNumsTobeFiltered.size() - 1) {
                    sql.append(", ");
                }
                i++;
            }
            sql.append(")");

            List<String> result = DBOperationUtil.commonQueryAll4FirstColumn(sql.toString(), new String[] {});
            LOGGER.debug(
                    MessageFormat.format("PartUtil.filterInvalidPartNums(): sql=[{0}] =>\n result={1}", sql, result));

            validPartNums.addAll(result);
        } catch (Exception e) {
            LOGGER.error("Some error occurred when filtering invalid Part numbers by sql: " + sql, e);
        }
        return validPartNums;
    }
    
    
    /**
     * Get parent PN by substitute PN through retrieve the latest bom of parent part
     * @param substitutePN
     * @return
     */
    public static List<String> getParentPNBySubstitute(String substitutePN) {
        LOGGER.debug("Enter => PartUtil.getParentAndChildPNBySubstitute()");

        List<String> parentPNList = new ArrayList<String>();
        StringBuffer sql = new StringBuffer();
        try {
            sql.append("select pm.wtpartnumber ");
            sql.append("from eplm.wtpart p, ");
            sql.append("eplm.wtpartmaster pm, ");
            sql.append("eplm.wtpartmaster cm, ");
            sql.append("eplm.wtpartmaster sm, ");
            sql.append("eplm.wtpartusagelink ul, ");
            sql.append("eplm.wtpartsubstitutelink sul ");
            sql.append("where ul.ida3a5=p.ida2a2 ");
            sql.append("and ul.ida3b5=cm.ida2a2 ");
            sql.append("and sul.ida3a5=ul.ida2a2 ");
            sql.append("and sul.ida3b5=sm.ida2a2 ");
            sql.append("and p.versionsortida2versioninfo=(select max(p1.versionsortida2versioninfo) from eplm.wtpart p1 where p1.ida3masterreference=p.ida3masterreference) ");
            sql.append("and p.latestiterationinfo=1 ");
            sql.append("and p.ida3masterreference=pm.ida2a2 ");
            sql.append("and sm.wtpartnumber = ? ");

            parentPNList = DBOperationUtil.commonQueryAll(sql.toString(), new String[] {substitutePN});
            LOGGER.debug(MessageFormat.format("PartUtil.getParentAndChildPNBySubstitute(): sql=[{0}] =>\n result={1}", sql, parentPNList));

        } catch (Exception e) {
            LOGGER.error("Some error occurred when filtering invalid Part numbers by sql: " + sql, e);
        }
        
        return parentPNList;
    }

}
